﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ICeFramework.DataManagement.Interfaces;
using Microsoft.SqlServer.Management.Smo;
using System.ComponentModel;
using System.Data;

namespace ICeFramework.DataManagement.MSSql
{
    public class MSSqlDataType : IDataType
    {
        private string _sqlDataTypeName;
        private DataType _type;
        private DbType _dbType;
        private string _systemTypeString;
        private bool _nullable;

        #region public MSSqlDataType(DataType type)
        /// <summary>
        /// Initializes a new instance of the <b>MSSqlDataType</b> class.
        /// </summary>
        /// <param name="type"></param>
        public MSSqlDataType(DataType type, bool nullable)
        {
            _nullable = nullable;            
            _type = type;
            _enclosedDataType = new MSSqlServerDataType(type.SqlDataType);
            _name = type.Name;
            _schema = type.Schema;
            SetTypes(type.SqlDataType, _nullable);
            _sqlDataTypeName = GetName(type.SqlDataType);
        }
        #endregion

        private void SetTypes(SqlDataType type, bool nullable)
        {
            switch (_type.SqlDataType)
            {
                case SqlDataType.BigInt:
                    _systemTypeString = "Int64" + _systemTypeString;
                    _dbType = DbType.Int64;
                    break;
                case SqlDataType.Binary:
                    _systemTypeString = "Byte[]" + _systemTypeString;
                    _dbType = DbType.Binary;
                    _maximumLength = _type.MaximumLength;
                    break;
                case SqlDataType.Bit:
                    _systemTypeString = "Boolean" + _systemTypeString;
                    _dbType = DbType.Boolean;
                    break;
                case SqlDataType.Char:
                    _systemTypeString = "String" + _systemTypeString;
                    _dbType = DbType.String;
                    _maximumLength = _type.MaximumLength;
                    break;
                case SqlDataType.DateTime:
                    _systemTypeString = "DateTime" + _systemTypeString;
                    _dbType = DbType.DateTime;
                    break;
                case SqlDataType.Decimal:
                    _systemTypeString = "Decimal" + _systemTypeString;
                    _dbType = DbType.Decimal;
                    _numericPrecision = _type.NumericPrecision;
                    _numericPrecision = _type.NumericScale;
                    break;
                case SqlDataType.Float:
                    _systemTypeString = "Double" + _systemTypeString;
                    _dbType = DbType.Double;
                    break;
                case SqlDataType.Image:
                    _systemTypeString = "Byte[]" + _systemTypeString;
                    _dbType = DbType.Binary;
                    break;
                case SqlDataType.Int:
                    _systemTypeString = "Int32" + _systemTypeString;
                    _dbType = DbType.Int32;
                    break;
                case SqlDataType.Money:
                    _systemTypeString = "Decimal" + _systemTypeString;
                    _dbType = DbType.Decimal;
                    break;
                case SqlDataType.NChar:
                    _systemTypeString = "String" + _systemTypeString;
                    _dbType = DbType.StringFixedLength;
                    _maximumLength = _type.MaximumLength;
                    break;
                case SqlDataType.None:
                    _systemTypeString = "Object" + _systemTypeString;
                    _dbType = DbType.Object;
                    break;
                case SqlDataType.NText:
                    _systemTypeString = "String" + _systemTypeString;
                    _dbType = DbType.String;
                    break;
                case SqlDataType.Numeric:
                    _systemTypeString = "Decimal" + _systemTypeString;
                    _dbType = DbType.Decimal;
                    _numericPrecision = _type.NumericPrecision;
                    _numericPrecision = _type.NumericScale;
                    break;
                case SqlDataType.NVarChar:
                    _systemTypeString = "String" + _systemTypeString;
                    _dbType = DbType.String;
                    _maximumLength = _type.MaximumLength;
                    break;
                case SqlDataType.NVarCharMax:
                    _systemTypeString = "String" + _systemTypeString;
                    _dbType = DbType.String;
                    break;
                case SqlDataType.Real:
                    _systemTypeString = "Single" + _systemTypeString;
                    _dbType = DbType.Single;
                    break;
                case SqlDataType.SmallDateTime:
                    _systemTypeString = "DateTime" + _systemTypeString;
                    _dbType = DbType.DateTime;
                    break;
                case SqlDataType.SmallInt:
                    _systemTypeString = "Int16" + _systemTypeString;
                    _dbType = DbType.Int16;
                    break;
                case SqlDataType.SmallMoney:
                    _systemTypeString = "Decimal" + _systemTypeString;
                    _dbType = DbType.Decimal;
                    break;
                case SqlDataType.SysName:
                    _systemTypeString = "Object" + _systemTypeString;
                    _dbType = DbType.Object;
                    break;
                case SqlDataType.Text:
                    _systemTypeString = "String" + _systemTypeString;
                    _dbType = DbType.String;
                    break;
                case SqlDataType.Timestamp:
                    _systemTypeString = "Byte[]" + _systemTypeString;
                    _dbType = DbType.Binary;
                    break;
                case SqlDataType.TinyInt:
                    _systemTypeString = "Byte" + _systemTypeString;
                    _dbType = DbType.Byte;
                    break;
                case SqlDataType.UniqueIdentifier:
                    _systemTypeString = "Guid" + _systemTypeString;
                    _dbType = DbType.Guid;
                    break;
                case SqlDataType.UserDefinedDataType:
                    _systemTypeString = "Object" + _systemTypeString;
                    _dbType = DbType.Object;
                    break;
                case SqlDataType.UserDefinedType:
                    _systemTypeString = "Object" + _systemTypeString;
                    _dbType = DbType.Object;
                    break;
                case SqlDataType.VarBinary:
                    _systemTypeString = "Byte[]" + _systemTypeString;
                    _dbType = DbType.Binary;
                    _maximumLength = _type.MaximumLength;
                    break;
                case SqlDataType.VarBinaryMax:
                    _systemTypeString = "Byte[]" + _systemTypeString;
                    _dbType = DbType.Binary;
                    break;
                case SqlDataType.VarChar:
                    _systemTypeString = "String" + _systemTypeString;
                    _dbType = DbType.String;
                    _maximumLength = _type.MaximumLength;
                    break;
                case SqlDataType.VarCharMax:
                    _systemTypeString = "String" + _systemTypeString;
                    _dbType = DbType.String;
                    break;
                case SqlDataType.Variant:
                    _systemTypeString = "Object" + _systemTypeString;
                    _dbType = DbType.Object;
                    break;
                case SqlDataType.Xml:
                    _systemTypeString = "Object" + _systemTypeString;
                    _dbType = DbType.Xml;
                    break;
                default:
                    _systemTypeString = "Object" + _systemTypeString;
                    _dbType = DbType.Object;
                    break;
            }
            if (!nullable)
            {
                _systemTypeString= _systemTypeString.Replace("?", "");
            }
        }

        private int? _maximumLength = null;
        #region public int MaximumLength
        /// <summary>
        /// Gets the MaximumLength of the MSSqlDataType
        /// </summary>
        /// <value></value>
        public int? MaximumLength
        {
            get { return _maximumLength; }
        }
        #endregion

        private string _name;
        #region public string Name
        /// <summary>
        /// Gets the Name of the MSSqlDataType
        /// </summary>
        /// <value></value>
        public string Name
        {
            get { return _name; }
        }
        #endregion

        private int? _numericPrecision = null;
        #region public int NumericPrecision
        /// <summary>
        /// Get/Sets the NumericPrecision of the MSSqlDataType
        /// </summary>
        /// <value></value>
        public int? NumericPrecision
        {
            get
            {
                return _numericPrecision;
            }
            set
            {
                _numericPrecision = value;
            }
        }
        #endregion

        private int? _numericScale = null;
        #region public int NumericScale
        /// <summary>
        /// Get/Sets the NumericScale of the MSSqlDataType
        /// </summary>
        /// <value></value>
        public int? NumericScale
        {
            get
            {
                return _numericScale;
            }
            set
            {
                _numericScale = value;
            }
        }
        #endregion

        private string _schema;
        #region public string Schema
        /// <summary>
        /// Get/Sets the Schema of the MSSqlDataType
        /// </summary>
        /// <value></value>
        public string Schema
        {
            get
            {
                return _schema;
            }
            set
            {
                _schema = value;
            }
        }
        #endregion

        private ISqlDataType _enclosedDataType;
        #region public ISqlDataType EnclosedDataType
        /// <summary>
        /// Get/Sets the SqlDataType of the MSSqlDataType
        /// </summary>
        /// <value></value>
        public ISqlDataType EnclosedDataType
        {
            get
            {
                return _enclosedDataType;
            }
            set
            {
                _enclosedDataType = value;
            }
        }
        #endregion

        #region private string GetName(SqlDataType enclosedType)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="enclosedType"></param>
        /// <returns></returns>
        private string GetName(SqlDataType enclosedType)
        {
            string typeName = enclosedType.ToString().ToLower();
            switch (enclosedType)
            {
                case SqlDataType.BigInt:
                    return string.Format("{0}", typeName);
                case SqlDataType.Binary:
                    return string.Format("{0}({1})", typeName, _maximumLength);
                case SqlDataType.Bit:
                    return string.Format("{0}", typeName);
                case SqlDataType.Char:
                    return string.Format("{0}({1})", typeName, _maximumLength);
                case SqlDataType.DateTime:
                    return string.Format("{0}", typeName);
                case SqlDataType.Decimal:
                    return string.Format("{0}({1}, {2})", typeName, _numericPrecision, _numericScale);
                case SqlDataType.Float:
                    return string.Format("{0}", typeName);
                case SqlDataType.Image:
                    return string.Format("{0}", typeName);
                case SqlDataType.Int:
                    return string.Format("{0}", typeName);
                case SqlDataType.Money:
                    return string.Format("{0}", typeName);
                case SqlDataType.NChar:
                    return string.Format("{0}({1})", typeName, _maximumLength);
                case SqlDataType.None:
                    return string.Format("{0}", typeName);
                case SqlDataType.NText:
                    return string.Format("{0}", typeName);
                case SqlDataType.Numeric:
                    return string.Format("{0}({1}, {2})", typeName, _numericPrecision, _numericScale);
                case SqlDataType.NVarChar:
                    return string.Format("{0}({1})", typeName, _maximumLength);
                case SqlDataType.NVarCharMax:
                    return string.Format("{0}(MAX)", typeName);
                case SqlDataType.Real:
                    return string.Format("{0}", typeName);
                case SqlDataType.SmallDateTime:
                    return string.Format("{0}", typeName);
                case SqlDataType.SmallInt:
                    return string.Format("{0}", typeName);
                case SqlDataType.SmallMoney:
                    return string.Format("{0}", typeName);
                case SqlDataType.SysName:
                    return string.Format("{0}", typeName);
                case SqlDataType.Text:
                    return string.Format("{0}", typeName);
                case SqlDataType.Timestamp:
                    return string.Format("{0}", typeName);
                case SqlDataType.TinyInt:
                    return string.Format("{0}", typeName);
                case SqlDataType.UniqueIdentifier:
                    return string.Format("{0}", typeName);
                case SqlDataType.UserDefinedDataType:
                    return string.Format("{0}", typeName);
                case SqlDataType.UserDefinedType:
                    return string.Format("{0}", typeName);
                case SqlDataType.VarBinary:
                    return string.Format("{0}({1})", typeName, _maximumLength);
                case SqlDataType.VarBinaryMax:
                    return string.Format("{0}(MAX)", typeName);
                case SqlDataType.VarChar:
                    return string.Format("{0}({1})", typeName, _maximumLength);
                case SqlDataType.VarCharMax:
                    return string.Format("{0}(MAX)", typeName);
                case SqlDataType.Variant:
                    return string.Format("{0}", typeName);
                case SqlDataType.Xml:
                    return string.Format("{0}", typeName);
                default:
                    return "Not Available";
            }
        }
        #endregion

        #region public override string ToString()
        /// <summary>
        /// Returns a <see cref="string"/> that represents the current <see cref="ICeFramework.DataAccess.DataAccess.MSSql.MSSqlDataType"/>.
        /// </summary>
        /// <returns>A <see cref="string"/> that represents the current <see cref="ICeFramework.DataAccess.DataAccess.MSSql.MSSqlDataType"/>.</returns>
        public override string ToString()
        {
            return _sqlDataTypeName;
        }
        #endregion

        #region public string ToSystemDataType(bool nullable)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="nullable"></param>
        /// <returns></returns>
        public string ToSystemDataType(bool nullable)
        {
            return _systemTypeString;
            //string sRet = nullable ? "?" : "";
            //switch (_type.SqlDataType)
            //{
            //    case SqlDataType.BigInt:
            //        sRet = "Int64" + sRet;
            //        break;
            //    case SqlDataType.Binary:
            //        sRet = "Byte[]" + sRet;
            //        break;
            //    case SqlDataType.Bit:
            //        sRet = "Boolean" + sRet;
            //        break;
            //    case SqlDataType.Char:
            //        sRet = "String" + sRet;
            //        break;
            //    case SqlDataType.DateTime:
            //        sRet = "DateTime" + sRet;
            //        break;
            //    case SqlDataType.Decimal:
            //        sRet = "Decimal" + sRet;
            //        break;
            //    case SqlDataType.Float:
            //        sRet = "Double" + sRet;
            //        break;
            //    case SqlDataType.Image:
            //        sRet = "Byte[]" + sRet;
            //        break;
            //    case SqlDataType.Int:
            //        sRet = "Int32" + sRet;
            //        break;
            //    case SqlDataType.Money:
            //        sRet = "Decimal" + sRet;
            //        break;
            //    case SqlDataType.NChar:
            //        sRet = "String" + sRet;
            //        break;
            //    case SqlDataType.None:
            //        sRet = "Object" + sRet;
            //        break;
            //    case SqlDataType.NText:
            //        sRet = "String" + sRet;
            //        break;
            //    case SqlDataType.Numeric:
            //        sRet = "Decimal" + sRet;
            //        break;
            //    case SqlDataType.NVarChar:
            //        sRet = "String" + sRet;
            //        break;
            //    case SqlDataType.NVarCharMax:
            //        sRet = "String" + sRet;
            //        break;
            //    case SqlDataType.Real:
            //        sRet = "Single" + sRet;
            //        break;
            //    case SqlDataType.SmallDateTime:
            //        sRet = "DateTime" + sRet;
            //        break;
            //    case SqlDataType.SmallInt:
            //        sRet = "Int16" + sRet;
            //        break;
            //    case SqlDataType.SmallMoney:
            //        sRet = "Decimal" + sRet;
            //        break;
            //    case SqlDataType.SysName:
            //        sRet = "Object" + sRet;
            //        break;
            //    case SqlDataType.Text:
            //        sRet = "String" + sRet;
            //        break;
            //    case SqlDataType.Timestamp:
            //        sRet = "Byte[]" + sRet;
            //        break;
            //    case SqlDataType.TinyInt:
            //        sRet = "Byte" + sRet;
            //        break;
            //    case SqlDataType.UniqueIdentifier:
            //        sRet = "Guid" + sRet;
            //        break;
            //    case SqlDataType.UserDefinedDataType:
            //        sRet = "Object" + sRet;
            //        break;
            //    case SqlDataType.UserDefinedType:
            //        sRet = "Object" + sRet;
            //        break;
            //    case SqlDataType.VarBinary:
            //        sRet = "Byte[]" + sRet;
            //        break;
            //    case SqlDataType.VarBinaryMax:
            //        sRet = "Byte[]" + sRet;
            //        break;
            //    case SqlDataType.VarChar:
            //        sRet = "String" + sRet;
            //        break;
            //    case SqlDataType.VarCharMax:
            //        sRet = "String" + sRet;
            //        break;
            //    case SqlDataType.Variant:
            //        sRet = "Object" + sRet;
            //        break;
            //    case SqlDataType.Xml:
            //        sRet = "Object" + sRet;
            //        break;
            //    default:
            //        sRet = "Object" + sRet;
            //        break;
            //}

            //if (!nullable)
            //{
            //    sRet = sRet.Replace("?", "");
            //}
            //return sRet;
        }
        #endregion

        #region public DbType DbType
        /// <summary>
        /// Gets the DbType of the MSSqlDataType
        /// </summary>
        /// <value></value>
        public DbType DbType
        {
            get { return _dbType; }
        }
        #endregion
    }
}
